package com.appspot.theonionorthenews;

import java.io.Serializable;

import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;

/**
 * An individual news (or parody news) item.
 */
@Entity
public class NewsItemEntity implements Serializable {

  /** We really only care about serialization for memcache.... */
  private static final long serialVersionUID = 1L;

  /** Primary key, absolutely stable and immutable. */
  @Id
  @GeneratedValue(strategy = GenerationType.IDENTITY)
  private Long id;

  /* TODO: We "should" have news items in an entity group (even if only one
   * global group exists), so we don't need cross-group transactions.  But
   * this formulation is giving me trouble...
   * 
  @ManyToOne
  @JoinColumn(nullable=false, name="group", referencedColumnName="name")
  private NewsGroup group;
   */

  /**
   * A semi-stable id for random selection.  As old or too-easy items are
   * retired, this field may be cleared; as holes are discovered in the draw
   * id set, high-draw items will be reassigned to "hold" ids, so this is,
   * slowly, unstable.
   */
  private Long drawId;

  /** The quiz quote. */
  private String quote;

  /** The citation URL */
  private String url;

  /** Truth-or-parody, although this can maybe be infered by contains("theonion.com") */
  private boolean isTrue;

  /** Metric counter, number of times players guessed (right or wrong). */
  private long guessCount;

  /** Metric counter, number of times correctly guessed. */
  private long correctCount;

  /** Submitter of the quote, for possible abuse tracking or for scoring */
  private String submitter;

  /**
   * Creates a new news item with zero'ed counters, {@link #isTrue} inferred from
   * the {@code url}, and id's temporarily unassigned.
   *
   * @param quote the quote to be displayed
   * @param url the source URL
   * @param submitter the user to credit with finding the quote
   */
  public NewsItemEntity(String quote, String url, String submitter) {
    this.quote = quote;
    this.url = url;
    this.submitter = submitter;
    isTrue = !url.startsWith("http://www.theonion.com/");
    guessCount = 0;
    correctCount = 0;
    drawId = null;
    id = null;
  }

  /** Default constructor for JPA */
  NewsItemEntity() {
    // leaves fields unset
  }

  public void clearDrawId() {
    drawId = null;
  }

  @Override
  public boolean equals(Object o) {
    if (o == null || !(o instanceof NewsItemEntity)) {
      return false;
    }
    NewsItemEntity other = (NewsItemEntity) o;

    // Because id is assigned late (in the data store), we allow a null id
    // as wildcard, equal to anything.  So they are UNequal only if both are
    // set, but to different values.
    if (id != null && other.id != null && !id.equals(other.id)) {
      return false;
    }

    // By contrast, we assign drawId, and it has to be equal, or both null.
    if (drawId == null && other.drawId != null) {
      return false;  // null to non-null: fail.
    } else if (!drawId.equals(other.drawId)) {
      return false;  // non-null, but not equal (or other is null): fail.
    }

    // The string properties are easy; they're just done on equality.
    if (!quote.equals(other.quote)) {
      return false;
    }
    if (!url.equals(other.url)) {
      return false;
    }
    if (!submitter.equals(other.submitter)) {
      return false;
    }
    assert isTrue == other.isTrue;  // since it's derived from URL, which is equal

    // We don't check guessCount and correctCount, which aren't about identity.
    // But should we?
    return true;
  }

  public Long getDrawId() {
    return drawId;
  }

  public void setDrawId(long draw) {
    drawId = draw;
  }

  public long getId() {
    if (id == null) {
      return -1;
    }
    return id;
  }

  public String getQuote() {
    return quote;
  }

  public String getUrl() {
    return url;
  }

  public String getSubmitter() {
    return submitter;
  }

  public long getGuessCount() {
    return guessCount;
  }

  public long getCorrectGuessCount() {
    return correctCount;
  }

  public void incrementGuesses(boolean correct) {
    guessCount++;
    if (correct) {
      correctCount++;
    }
  }

  public boolean isTrue() {
    return isTrue;
  }

  /** Assigns the news item to a particular group. */
  void setGroup(NewsGroupEntity group) {
    // TODO: this.group = group;
  }
}